import { Config, Provide, InjectClient, Context, Inject } from '@midwayjs/core';
import { IWxAppInfo } from '../interface';
import { CachingFactory, MidwayCache } from '@midwayjs/cache-manager';
import got from 'got';
import Utils from '../utils';

@Provide()
export class WxService {
  /**
   * access_token缓存键名
   */
  public static CACHE_KEY_ACCESS_TOKEN = 'CACHE_KEY_ACCESS_TOKEN';

  /**
   * 微信获取手机号错误
   */
  public static readonly WX_GET_PHONE_ERROR = {
    '-1': '系统繁忙，此时请开发者稍候再试',
    '40029': 'js_code无效',
    '45011': 'API 调用太频繁，请稍候再试，每个用户每分钟1次',
    '40013': '请求appid身份与获取code的小程序appid不匹配'
  };

  /**
   * 微信登录错误
   */
  public static readonly WX_LOGIN_ERROR = {
    '-1': '系统繁忙，此时请开发者稍候再试',
    '40029': 'js_code无效',
    '45011': 'API 调用太频繁，请稍候再试',
    '40226': '高风险等级用户，小程序登录拦截，详情见微信用户安全解决方案'
  };

  @Inject()
  private ctx: Context;

  @Inject()
  private utils: Utils;

  @Config('wxapp')
  private wxapp: IWxAppInfo;

  @InjectClient(CachingFactory, 'default')
  cache: MidwayCache;

  /**
   * 获取用户手机号
   * @param js_code
   * @returns
   */
  async getUserPhoneNumber(js_code: string) {
    try {
      const access_token = (await this.getWxAccessToken()).data;
      const json = await got
        .post(this.wxapp.phone_url + '?access_token=' + access_token, {
          json: {
            code: js_code
          }
        })
        .json<{
          phone_info: {
            phoneNumber: string;
            purePhoneNumber: string;
            countryCode: string;
            watermark: { timestamp: number; appid: string };
          };
          errcode: number;
          errmsg: string;
        }>();

      if (json.errcode === 0) {
        return this.utils.response.success<typeof json.phone_info>(
          json.phone_info
        );
      } else {
        return this.utils.response.fail(
          WxService.WX_GET_PHONE_ERROR[json.errcode] || '获取用户手机号失败',
          json.errcode
        );
      }
    } catch (error) {
      this.ctx.logger.error('获取用户手机号失败', error);
      return this.utils.response.fail('获取用户手机号失败');
    }
  }

  /**
   * 微信登录
   * @param js_code
   * @returns
   */
  async wxAppLogin(js_code: string) {
    try {
      const searchParams = {
        appid: this.wxapp.appid,
        secret: this.wxapp.secret,
        grant_type: this.wxapp.login_grant_type,
        js_code
      };

      const json = await got
        .get(this.wxapp.login_url, {
          searchParams
        })
        .json<{
          openid: string;
          session_key: string;
          unionid: string;
          errcode: number;
          errmsg: string;
        }>();
      if (json.openid && json.session_key) {
        return this.utils.response.success<typeof json>(json);
      } else {
        return this.utils.response.fail(
          WxService.WX_LOGIN_ERROR[json.errcode] || '微信登录失败',
          json.errcode
        );
      }
    } catch (error) {
      this.ctx.logger.error('微信登录失败', error);
      return this.utils.response.fail('微信登录失败');
    }
  }

  /**
   * 获取微信access_token
   * @returns
   */
  async getWxAccessToken() {
    const cache = await this.cache.get<string>(
      WxService.CACHE_KEY_ACCESS_TOKEN
    );
    if (cache) {
      this.ctx.logger.info('从缓存中获取access_token', cache);
      return this.utils.response.success<string>(cache);
    }

    try {
      this.ctx.logger.info('从微信获取access_token');
      const json = await got
        .post(this.wxapp.token_url, {
          json: {
            appid: this.wxapp.appid,
            secret: this.wxapp.secret,
            grant_type: this.wxapp.token_grant_type
          }
        })
        .json<{ access_token: string; expires_in: number }>();
      if (json.access_token && json.expires_in) {
        await this.cache.set(
          WxService.CACHE_KEY_ACCESS_TOKEN,
          json.access_token,
          (json.expires_in - 120) * 1000
        );
      }
      this.ctx.logger.info('从微信获取access_token成功', json.access_token);
      return this.utils.response.success(json.access_token);
    } catch (error) {
      this.ctx.logger.error('获取微信access_token失败', error);
      return this.utils.response.fail('获取微信access_token失败');
    }
  }
}
